﻿//
// *************************************************************************************
// WP-Framework
//
// Developed by 
// Lukas Kretschmar (lkretschmar.dev@bluewin.ch)
//
// Switzerland, Copyright (c) 2013 lkretschmar
// This content is released under the MIT License (http://opensource.org/licenses/MIT).
//     
// *************************************************************************************
//
//
			

using System;
using System.Linq;
using Expression = System.Linq.Expressions.Expression;
using System.Linq.Expressions;
using KretschIT.WP_Fx.Core.Validation;
using KretschIT.WP_Fx.EasyMoq.I18n;
using KretschIT.WP_Fx.Core.Extensions;

namespace KretschIT.WP_Fx.EasyMoq.Matchers
{
    internal class DefaultMatcherFactory : IMatcherFactory
    {
        public IMatcher Create(Expression expression)
        {
            It.IsNotNull(() => expression);

            if (expression is ConstantExpression)
            {
                return this.GetConstantMatcher(expression as ConstantExpression);
            }
            else if (expression is MemberExpression)
            {
                return this.GetMemberMatcher(expression as MemberExpression);
            }
            else if (expression is MethodCallExpression)
            {
                return this.GetMethodCallMatcher(expression as MethodCallExpression);
            }
            else if (expression is NewExpression)
            {
                return this.GetNewInstancceMatcher(expression as NewExpression);
            }
            throw new NotSupportedException(this.GetNotSupportedMessageFor(expression.GetType()));
            
        }

        #region ConstantMatcher

        private IMatcher GetConstantMatcher(ConstantExpression constant)
        {
            return new ArgumentMatcher(constant);
        }

        #endregion

        #region MemberMatcher

        private IMatcher GetMemberMatcher(MemberExpression member)
        {
            var expr = Expression.Constant(member.GetValue());
            return new ArgumentMatcher(expr);
        }

        #endregion

        #region MethodCallMatcher

        private IMatcher GetMethodCallMatcher(MethodCallExpression call)
        {
            if (call.Method.Name == GetMethodName<bool>(() => Allow.Only<bool>(s => s)))
            {
                return new OnlyMatcher(call);
            }
            else if (call.Method.Name == GetMethodName<bool>(() => Allow.Any<bool>()))
            {
                return new AnyMatcher(call);
            }
            throw new NotSupportedException(this.GetNotSupportedMessageFor(typeof(MethodCallExpression)));
        }

        private string GetMethodName<T>(Expression<Func<T>> call)
        {
            return (call.Body as MethodCallExpression).Method.Name;
        }

        #endregion

        #region NewMatcher

        private IMatcher GetNewInstancceMatcher(NewExpression newExpression)
        {
            var args = newExpression.Arguments.Select(e => Expression.Lambda(e).Compile().DynamicInvoke());
            var instance = newExpression.Constructor.Invoke(args.ToArray());
            var expr = Expression.Constant(instance);
            return new ArgumentMatcher(expr);
        }

        #endregion

        private string GetNotSupportedMessageFor(Type type)
        {
            return string.Format(Messages.Exception_MissingMatcherFactoryFormat, type.FullName);
        }
    }
}
